#pragma once
#include <pthread.h>
#include <unordered_map>
#include <vector>
#include "Util.hpp"
#include "Config.hpp"

namespace cloud
{
    typedef struct BackupInfo_t
    {
    public:
        BackupInfo_t()
        {}
        bool pack_flag;
        size_t fsize;
        time_t atime;          // 最后一次访问时间
        time_t mtime;          // 最后一次修改时间
        std::string real_path; // 文件实际存储路径
        std::string pack_path; // 压缩包存储路径
        std::string url_path;
        void NewBackupInfo(const std::string &realpath)
        {
            pack_flag = false;
            FileUtil fu(realpath);
            fsize = fu.FileSize();
            atime = fu.LastATime();
            mtime = fu.LastMTime();
            real_path = realpath;
            Config *instance = Config::GetInstance();

            pack_path = instance->GetPackDir() + fu.FileName() + instance->GetPackFileSuffix();
            url_path = instance->GetDownloadPrefix() + fu.FileName();
        }
    } BackupInfo;
    class DataManager
    {
    private:
        std::string _backup_file;
        std::unordered_map<std::string, BackupInfo> _table;
        pthread_rwlock_t _rwlock;

    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            pthread_rwlock_init(&_rwlock, nullptr);

            InitLoad();
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }
        bool Storage()
        {
            std::vector<BackupInfo> arry;
            GetAll(&arry);
            Json::Value root;
            for(auto e : arry)
            {
                Json::Value item;
                item["pack_flag"] = e.pack_flag;
                item["fsize"] = (Json::Int64)e.fsize;
                item["atime"] = (Json::Int64)e.atime;
                item["mtime"] = (Json::Int64)e.mtime;
                item["real_path"] = e.real_path;
                item["pack_path"] = e.pack_path;
                item["url_path"] = e.url_path;
                root.append(item);
            }
            std::string body;
            if(JsonUtil::Serialize(root, &body) == false)
            {
                std::cout << "storage serialize error\n";
                return false;                
            }
            FileUtil fu(_backup_file);
            if(fu.SetContent(body) == false)
            {
                std::cout << "storage setcontent error\n";
                return false;
            }
            return true;
        }
        bool InitLoad()
        {
            FileUtil fu(_backup_file);
            if(fu.Exists() == false)
            {
                return true;
            }

            std::string body;
            fu.GetContent(&body);
            Json::Value root;
            JsonUtil::UnSerialize(body, &root);
            for(int i = 0; i < root.size(); i++)
            {
                BackupInfo info;
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.atime = root[i]["atime"].asInt64();
                info.mtime = root[i]["mtime"].asInt64();
                info.fsize = root[i]["fsize"].asInt64();
                info.real_path = root[i]["real_path"].asString();
                info.pack_path = root[i]["pack_path"].asString();
                info.url_path = root[i]["url_path"].asString();
                Insert(info);
            }
            return true;
        }
        bool Insert(const BackupInfo &info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url_path] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();
            return true;
        }
        bool Update(const BackupInfo &info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url_path] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();
            return true;
        }
        bool GetOneByUrl(const std::string &url, BackupInfo *info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.find(url);
            if (it == _table.end())
            {
                std::cout << "no find file\n";
                pthread_rwlock_unlock(&_rwlock);
                return false;
            }
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        bool GetOneRealpath(const std::string &realpath, BackupInfo *info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            for (auto e : _table)
            {
                if (e.second.real_path == realpath)
                {
                    *info = e.second;
                    pthread_rwlock_unlock(&_rwlock);
                    return true;
                }
            }
            pthread_rwlock_unlock(&_rwlock);
            return false;
        }
        bool GetAll(std::vector<BackupInfo> *arry)
        {
            pthread_rwlock_wrlock(&_rwlock);
            for (auto e : _table)
            {
                arry->push_back(e.second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
    };
}